<%@page import="
java.io.IOException,
java.util.Map,
java.util.HashMap,
javax.script.*, 
sun.org.mozilla.javascript.internal.*" %>

<%
class JS_ScriptEngine extends javax.script.ScriptEngineManager{
	private javax.script.ScriptEngine js_engine = 
		   (javax.script.ScriptEngine) this.getEngineByExtension("js"); 
	
	// Invocable接口是 ScriptEngine可选实现的接口。（多态？？？）
	private javax.script.Invocable inv = (javax.script.Invocable)js_engine;  
	
	// ScriptEngine.eval("add(3,4)").toString()
	public Object eval(String jsSource){
		Object result = null;
		
		try{
			result = js_engine.eval(jsSource);
		}catch(javax.script.ScriptException e){}
		
		return result;
	}
	/*
		在 Java 中向脚本引擎 (Script Engine) 传递变量，即脚本语言用 java 的变量。
		当然，使用 eval() 也可以了。注意可以直接赋值 Java 对象。
	 */
	public void put(String varName, Object obj){
		// js_engine.put("fileObj", obj);
		js_engine.put(varName, obj);
	}
	
	/*
		对于上面put的变量，它作用于自身engine范围内，也就是ScriptContext.ENGINE_SCOPE，put 的变量放到一个叫Bindings的Map中，可以通过 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(“a”);得到put的内容。和ENGINE_SCOPE相对，还有个ScriptContext.GLOBAL_SCOPE 作用域，其作用的变量是由同一ScriptEngineFactory创建的所有ScriptEngine共享的全局作用域。
	 */
	public Object get(){
		javax.script.Bindings bindings = js_engine.getBindings(javax.script.ScriptContext.ENGINE_SCOPE);
		Object a = bindings.get("fileObj");
	
		return a;
	}
	
	public void read_jsFile(String filePath){
		// 从类路径中加载js文件并执行
		// engine.eval(new InputStreamReader(Main.class.getResourceAsStream("scripting.js")));
		// 简单方法
		System.out.println("Reading JS File:::::::::" + filePath);

		java.io.FileReader reader = null;
		try{
			if(!new java.io.File(filePath).exists())
				throw new java.io.FileNotFoundException("JS file not exist");
	
			reader  = new java.io.FileReader(filePath);	
			js_engine.eval(reader);
		}catch(javax.script.ScriptException e){
	//		log(e.toString(), true);
		}catch(java.io.FileNotFoundException e){
	//		log("没有 js 文件！", true);
		}finally{
			try{
				reader.close();
			}catch(java.io.IOException e){}
		}
	}
	
	/*
		脚本引擎本身并不负责执行在脚本中定义的方法，而是转换为 javax.script.Invocable 的对象 inv,有inv.invokeFunction() 方法来调用脚本的方法。invokeFunction() 方法的第一个参数必须是方法名，后面是参数列表。
	*/
	public Object call(String methodName){
		Object result = null;
			try { 
			    result = inv.invokeFunction(methodName, 1, 4);  
			    // OR
			    // Object result1 = jsInvoke.invokeFunction(methodName, new Object[] { 10, 5 });
			}catch(NoSuchMethodException ex) {  
				System.out.println("没有" + methodName + "这个方法");
	//			log(ex.toString());  
			}catch(javax.script.ScriptException ex) {  
				System.out.println("脚本执行异常！");
	//			log(ex.toString());  
			}

	
		return result;
	}
	
	/*
		脚本语言实现java的接口
		Invocable还有个很酷的功能，就是动态实现接口，它可以从脚本引擎中得到Java Interface 的实例；也就是说，可以定义个一个Java接口，其实现是由脚本完成。以上面的例子为例，定义接口JSLib，该接口中的函数和JavaScript中的函数签名保持一致：
		
		interface Adder {
		  int add(int a, int b);
		}
		Adder adder = inv.getInterface(Adder.class); // 通过 inv.getIntegerface()方法转化为java的接口
	
		// String script="var obj= new Object();obj.run=function(){println('run() was called');}";  
	    // engine.eval(script);  
	    // Object obj=engine.get("obj");  
	    // javax.script.Invocable inv=(Invocable)engine;  
		// Runnable r=inv.getInterface(obj, Runnable.class);  
		// Thread t=new Thread(r);
		// engine.eval("function run() {print('www.java2s.com');}");
		// 		Invocable invokeEngine = (Invocable) engine;
		// 		Runnable runner = invokeEngine.getInterface(Runnable.class);
		// 		Thread t = new Thread(runner);
		// 		t.start();
		// 		t.join();
	*/
	public Object apply(Class<?> cls){
		return inv.getInterface(cls);
	}
	
	/*
		脚本预编译
		脚本引擎默认是解释执行的，如果需要反复执行脚本，可以使用它的可选接口Compilable来编译执行脚本，以获得更好的性能
	 */
	public void compile(){
		try{
			javax.script.Compilable compEngine = (javax.script.Compilable) js_engine;
			javax.script.CompiledScript script = compEngine.compile("function max_num(a,b){return (a>b)?a:b;}");
			script.eval();
			javax.script.Invocable invoke = (javax.script.Invocable) compEngine;
			System.out.println(invoke.invokeFunction("max_num",4,6));
		}catch(javax.script.ScriptException e){
		}catch(NoSuchMethodException e){}
	}
	
	// 导入包
	// String jsCode = "importPackage(java.util);var list2 = Arrays.asList(['A', 'B', 'C']); ";
	// js_engine.eval(jsCode);
	// java.util.List<String> list2 = (java.util.List<String>) js_engine.get("list2");
	// for (String val : list2) {
	// 	System.out.println(val);
	// }
	
	public /* static */ Map<String, Object>[] getArr(NativeArray nativeArr)throws IOException{
	    if(nativeArr == null)return new HashMap[0];
	    
	    Long len = nativeArr.getLength();
	    
	    Map<String, Object>[] arr  = new HashMap[len.intValue()];
	    
	    for (Object o : nativeArr.getIds()) {
	        Integer index = (Integer) o;
	        NativeObject nativeObj = (NativeObject)nativeArr.get(index, nativeArr);
	        HashMap<String, Object> item = (HashMap<String, Object>) NativeObject2Hash(nativeObj);
	        
	        arr[index.intValue()] = item;
	    }
	
	    return arr;
	}
	
	public /* static */ Map<String, Object>[] getArr(Object obj)throws IOException{
	    if(obj != null && !obj.equals("")){
	        NativeArray _NativeArray = (NativeArray)obj;
	        return getArr(_NativeArray);
	    }else 
	    	return new HashMap[0];// 写成 new HashMap<String, Object>[0] 报错
	}
	
	// JS 对象 转换到 Java Hash 对象
	public /* static */ Map<String, Object> NativeObject2Hash(NativeObject obj){
	    Map<String, Object> _data = new HashMap<String, Object>();
	    if(obj == null)return null;
	    
	    // 遍历对象
	    Object[] ids = obj.getAllIds();
	    for (Object id : ids) {
	        String newId = (String) id;
	        _data.put(newId, obj.get(newId, obj));
	        // System.out.println("\n" + newId);
	    }
	    return _data;
	}
}
%>